use config::{FileFormat, Format, Map, Source, Value};
use std::fmt::Debug;

pub type File<T, F> = config::File<T, F>;
pub type Config = config::Config;
pub type Environment = config::Environment;
pub type ConfigError = config::ConfigError;
pub type HeaderMap = reqwest::header::HeaderMap;

/// 请求方法，只支持[GET,POST]两种
#[derive(Debug, Clone)]
pub enum HttpMethod {
    Get,
    Post,
}
impl Default for HttpMethod {
    fn default() -> Self {
        HttpMethod::Get
    }
}

/// http源
#[derive(Debug, Clone)]
pub struct Http {
    url: String,
    method: HttpMethod,
    format: FileFormat,
}

impl Http {
    pub fn new<S: AsRef<str>>(url: S, format: FileFormat, method: HttpMethod) -> Self {
        Http {
            url: url.as_ref().to_string(),
            format,
            method,
        }
    }
    pub fn with(url: &str, format: &str, method: &str) -> Self {
        Http {
            url: url.to_string(),
            format: Http::convert_format(format),
            method: Http::convert_method(method),
        }
    }

    fn convert_format(format: &str) -> FileFormat {
        match format {
            "toml" => FileFormat::Toml,
            "json" => FileFormat::Json,
            "yaml" => FileFormat::Yaml,
            "ini" => FileFormat::Ini,
            "ron" => FileFormat::Ron,
            "json5" => FileFormat::Json5,
            _ => unreachable!("不支持格式"),
        }
    }

    fn convert_method(method: &str) -> HttpMethod {
        match method {
            "get" => HttpMethod::Get,
            "post" => HttpMethod::Post,
            _ => unreachable!("没有启用特性"),
        }
    }
}

impl Source for Http {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<Map<String, Value>, ConfigError> {
        let client = match reqwest::blocking::Client::builder().build() {
            Ok(client) => client,
            Err(e) => return Err(ConfigError::Message(e.to_string())),
        };

        let response = match self.method {
            HttpMethod::Get => client.get(&self.url).send(),
            HttpMethod::Post => client.post(&self.url).send(),
        };

        let response = match response {
            Ok(response) => response,
            Err(e) => return Err(ConfigError::Message(e.to_string())),
        };

        let content = match response.text() {
            Ok(content) => content,
            Err(e) => return Err(ConfigError::Message(e.to_string())),
        };

        let format = self.format;

        format
            .parse(None, &content)
            .map_err(|cause| ConfigError::Message(cause.to_string()))
    }
}
